#include <model.h>
#include <Poco\Path.h>
#include <Poco\File.h>

#include <fstream>

model::model(string host, string user, string pass) {
  db.connect(host, user, pass);
}

model::~model() {
  close();
}

string 
model::getModelName() {
  return model_name;
}

string 
model::getDataStorageDir() {
  return data_storage_dir;
}

mysqlDatabase &
model::getDB() {
  return db;
}

bool
model::available(string _model_name, string _data_storage_dir) {

  Poco::Path dir(_data_storage_dir);
  dir.pushDirectory(_model_name);
  
  Poco::File model_file_def(Poco::Path(dir.absolute(), _model_name + ".def"));

  return model_file_def.exists();
}

bool
model::openModel(string _model_name, string _data_storage_dir) {
  
  model_name = _model_name;
  data_storage_dir = _data_storage_dir;

  return true;
}

bool
model::createModel(string _model_name, string _data_storage_dir) {

  Poco::Path dir(_data_storage_dir);
  dir.pushDirectory(_model_name);
  Poco::File file(dir);
  file.createDirectories();

  dir.pushDirectory("global_profile");
  dir.pushDirectory("trajectory_tiles");
  file = dir;
  file.createDirectories();
  dir.popDirectory();

  dir.pushDirectory("terrain_tiles");
  file = dir;
  file.createDirectories();
  dir.popDirectory();

  dir.pushDirectory("texture_tiles");
  file = dir;
  file.createDirectories();
  dir.popDirectory();

  dir.pushDirectory("content_tiles");
  file = dir;
  file.createDirectories();
  dir.popDirectory();

  dir.pushDirectory("geometry_tiles");
  file = dir;
  file.createDirectories();
  dir.popDirectory();

  dir.pushDirectory("user_profiles");
  file = dir;
  file.createDirectories();
  dir.popDirectory();

  // create empty model file
  Poco::File model_file(Poco::Path(dir.absolute(), _model_name + ".def"));
  model_file.createFile();

  return true;
}

bool 
model::addUser(string name, string password) {

  Poco::Path dir(data_storage_dir);
  dir.pushDirectory(model_name);
  dir.pushDirectory("user_profiles");
  dir.pushDirectory(name);
  dir.pushDirectory("trajectory_tiles");
  Poco::File file(dir);
  file.createDirectories();
  dir.popDirectory();

  dir.pushDirectory("terrain_tiles");
  file = dir;
  file.createDirectories();
  dir.popDirectory();

  dir.pushDirectory("texture_tiles");
  file = dir;
  file.createDirectories();
  dir.popDirectory();

  dir.pushDirectory("content_tiles");
  file = dir;
  file.createDirectories();
  dir.popDirectory();

  dir.pushDirectory("geometry_tiles");
  file = dir;
  file.createDirectories();
  dir.popDirectory();

  dir.pushDirectory("user_profiles");
  file = dir;
  file.createDirectories();
  dir.popDirectory();

  // create empty model file
  Poco::File model_file(Poco::Path(dir.absolute(), name + ".def"));
  model_file.createFile();

  ofstream output(model_file.path().c_str(), ios::out | ios::binary);
  output.write(password.c_str(), password.size());
  output.close();

  return true;
}

void
model::addModule(dataModule *module) {
  modules.push_back(module);
}

bool
model::update() {
  
  bool res = true;
  for (int i = 0; i != modules.size(); i++) {
    res = res && modules[i]->post(this);
  }

  return res;
}

void
model::close() {
  if (db.connected()) {
    db.closeConnection();
  }
}

#if 0
void
model::setName(string name) {
  model_name = name;
}

void
model::setHeightmapSize(vec2i size) {
  hm_size = size;
}

void
model::setHeightmapTileSize(vec2i size) {
  hm_tile_size = size;
}

void
model::setHeightmapLevels(int levels) {
  hm_levels = levels;
}

void
model::setTextureSize(vec2i size) {
  t_size = size;
}

void
model::setTextureTileSize(vec2i size) {
  t_tile_size = size;
}

void
model::setTextureLevels(int levels) {
  t_levels = levels;
}

void 
model::clearTerrainData() {
  _clearTerrainData = true;
}

void
model::clearTextureData() {
  _clearTextureData = true;
}

void 
model::addTerrain(terrain &_terrain) {
  terrains.push_back(_terrain);
}

void 
model::addTexture(texture &_texture) {
  textures.push_back(_texture);
}

mysqlDatabase &
model::getDB() {
  return db;
}

bool 
model::createDB() {
  if (model_name.empty()) {
    cerr << "Model name is not set. The model hasn't been created.";
    return false;
  }
    
  stringstream query;
  query << "CREATE database " << model_name;
  return db.executequery(query.str()); 
}

bool 
model::useDB() {

  if (model_name.empty()) {
    cerr << "model::useDB: Model name is not set." << endl;
    return false;
  }
    
  stringstream query;
  query << "USE " << model_name << endl;
  return db.executequery(query.str());  
}

bool 
model::createTables() {

  if (!useDB()) {
    cerr << "Creation of model's tables failed" << endl;
    return false;
  }

  stringstream query;
  query << "DROP TABLE IF EXISTS description" << endl;
  if (!db.executequery(query.str())) return false;
 
  query.str("");
  query << "CREATE TABLE description ("
        << "id integer unsigned UNIQUE NOT NULL AUTO_INCREMENT,"
        << "hm_width integer default 0,"
        << "hm_height integer default 0,"
        << "hm_tile_width integer default 0,"
        << "hm_tile_height integer default 0,"
        << "hm_levels integer default 0"
        << ")"
        << "ENGINE=InnoDB character set utf8 collate utf8_czech_ci";
  if (!db.executequery(query.str())) return false;

  for (int level = 0; level != hm_levels; level++) {
    query.str("");
    query << "CREATE TABLE hm_tiles_" << level << " ("
          << "id integer unsigned UNIQUE NOT NULL,"
          << "fk_parent_hm_tile integer unsigned,"
          << "data longblob,"
          << "PRIMARY KEY (id),"
          << "INDEX (id),"
          << "INDEX (fk_parent_hm_tile)"
          << ")"
          << "ENGINE=InnoDB character set utf8 collate utf8_czech_ci";
    if (!db.executequery(query.str())) return false;
  }

  for (int level = 0; level != hm_levels - 1; level++) {
    query.str("");
    query << "ALTER TABLE hm_tiles_" << level
          << " ADD FOREIGN KEY (fk_parent_hm_tile) REFERENCES hm_tiles_" << level + 1 << "(id) ON DELETE CASCADE ON UPDATE CASCADE";
    if (!db.executequery(query.str())) return false;
  }


  for (int level = 0; level != hm_levels; level++) {
    query.str("");
    query << "CREATE TABLE tex_tiles_" << level << " ("
          << "id integer unsigned UNIQUE NOT NULL,"
          << "fk_hm_tile integer unsigned NOT NULL,"
          << "data longblob,"
          << "PRIMARY KEY (id),"
          << "FOREIGN KEY (fk_hm_tile) REFERENCES hm_tiles_" << level << "(id) ON DELETE CASCADE ON UPDATE CASCADE,"
          << "INDEX (id),"
          << "INDEX (fk_hm_tile)"
          << ")"
          << "ENGINE=InnoDB character set utf8 collate utf8_czech_ci";
    if (!db.executequery(query.str())) return false;
  }

  return true;
}

bool 
model::addTerrainData() {

  if (!useDB()) {
    cerr << "Creation of model's tables failed" << endl;
    return false;
  }

  for (int t = 0; t != terrains.size(); t++) {
    terrains[t].addToDB(this, db);
  };


  return true;
}

bool 
model::addTextureData() {

  if (!useDB()) {
    cerr << "Creation of model's tables failed" << endl;
    return false;
  }

  for (int t = 0; t != textures.size(); t++) {
    textures[t].addToDB(this, db);
  };

  return false;
}

bool 
model::deleteModel() {
  if (model_name.empty()) {
    cerr << "Model name is not set." << endl;
    return false;
  }
    
  db.connect("", "", ""); // default connection to root:root@localhost

  stringstream query;
  query << "DROP database " << model_name << endl;
  return db.executequery(query.str());  
}

bool
model::createModel() {

  db.connect("", "", ""); // default connection to root:root@localhost

  if (!db.connected()) {
    cerr << "Connection to mysql server failed." << endl;
    return false;
  }

  /*if (!createDB()) {
    cerr << "Creation of database '" << model_name << "' failed." << endl;
    return false;
  }

  if (!createTables()) {
    cerr << "Creation of tables has failed." << endl;
    return false;
  }
  if (!addTerrainData()) {
    cerr << "Addition of terrain data into the model's db has failed." << endl;
    return false;
  }*/
  if (!addTextureData()) {
    cerr << "Addition of texture data into the model's db has failed." << endl;
    return false;
  }

  return true;
}
#endif